﻿using PlutoStudio.Application.Services;
using PlutoStudio.Aspects;
using PlutoStudio.Aspects;
using PlutoStudio.Authorization;
using PlutoStudio.BackgroundJobs;
using PlutoStudio.Configuration;
using PlutoStudio.Configuration.Startup;
using PlutoStudio.Dependency;
using PlutoStudio.Domain.UnitOfWork;
using PlutoStudio.Events.Bus;
using PlutoStudio.Events.Bus.Factories;
using PlutoStudio.Events.Bus.Handlers;
using PlutoStudio.Localization;
using PlutoStudio.Localization.Dictionaries;
using PlutoStudio.Localization.Dictionaries.Xml;
using PlutoStudio.Modules;
using PlutoStudio.Net.Mail;
using PlutoStudio.Reflection;
using PlutoStudio.Runtime;
using PlutoStudio.Runtime.Caching;
using PlutoStudio.Runtime.Remoting;
using PlutoStudio.Threading;
using PlutoStudio.Threading.BackgroundWorkers;
using System;
using System.IO;
using System.Linq.Expressions;

namespace PlutoStudio
{
    /// <summary>
    /// Kernel (core) module of the ABP system.
    /// No need to depend on this, it's automatically the first module always.
    /// </summary>
    public sealed class KernelModule : Module
    {
        public override void PreInitialize()
        {
            IocManager.AddConventionalRegistrar(new BasicConventionalRegistrar());
            IocManager.RegisterGeneric(typeof(IAmbientScopeProvider<>), typeof(DataContextAmbientScopeProvider<>), LifetimeScopeStyle.Transient);
            AddAuditingSelectors();
            AddLocalizationSources();
            AddSettingProviders();
            AddUnitOfWorkFilters();
            ConfigureCaches();
            AddIgnoredTypes();
        }

        public override void Initialize()
        {
            Logger.Info("KernelModule");
            IocManager.Install(new EventBusInstaller(IocManager));
            IocManager.RegisterAssemblyByConvention(typeof(KernelModule).Assembly,new ConventionalRegistrationConfig
            {
                 InstallInstallers=false
            });
            foreach (var replaceAction in ((StartupConfiguration)Configuration).ServiceReplaceActions.Values)
            {
                replaceAction();
            }
            InterceptorHelper.AddConventionalRegistrar(new UnitOfWorkInterceptorRegistrar());
        }

        public override void PostInitialize()
        {
            RegisterMissingComponents();
            IocManager.Resolve<SettingDefinitionManager>().Initialize();
            IocManager.Resolve<LocalizationManager>().Initialize();
            IocManager.Resolve<PermissionManager>().Initialize();
            //IocManager.Resolve<NotificationDefinitionManager>().Initialize();
            if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
            {
                var workerManager = IocManager.Resolve<IBackgroundWorkerManager>();
                workerManager.Start();
                workerManager.Add(IocManager.Resolve<IBackgroundJobManager>());
            }

        }


        public override void Shutdown()
        {
            if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
            {
                IocManager.Resolve<IBackgroundWorkerManager>().StopAndWaitToStop();
            }

        }

        private void AddUnitOfWorkFilters()
        {
            Configuration.UnitOfWork.RegisterFilter(DataFilters.SoftDelete, true);
        }

        private void AddSettingProviders()
        {
            Configuration.Settings.Providers.Add<LocalizationSettingProvider>();
            Configuration.Settings.Providers.Add<EmailSettingProvider>();
            //Configuration.Settings.Providers.Add<NotificationSettingProvider>();
            //Configuration.Settings.Providers.Add<TimingSettingProvider>();

        }

        private void AddAuditingSelectors()
        {
            Configuration.Auditing.Selectors.Add(
                new NamedTypeSelector(
                    "PlutoStudio.ApplicationServices",
                    type => typeof(IApplicationService).IsAssignableFrom(type)
                    ));
        }

        private void AddLocalizationSources()
        {
            Configuration.Localization.Sources.Add(
                new DictionaryBasedLocalizationSource(
                    Consts.LocalizationSourceName,
                    new XmlEmbeddedFileLocalizationDictionaryProvider(
                        typeof(KernelModule).Assembly, "PlutoStudio.Localization.Sources.XmlSource"
                    )));
        }

        private void ConfigureCaches()
        {
            Configuration.Caching.Configure(FrameworkCacheNames.ApplicationSettings, cache =>
            {
                cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8);
            });

            Configuration.Caching.Configure(FrameworkCacheNames.UserSettings, cache =>
            {
                cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(20);
            });
        }
        private void AddIgnoredTypes()
        {
            var commonIgnoredTypes = new[]
{
                typeof(Stream),
                typeof(Expression)
            };

            foreach (var ignoredType in commonIgnoredTypes)
            {
                Configuration.Auditing.IgnoredTypes.AddIfNotContains(ignoredType);
                Configuration.Validation.IgnoredTypes.AddIfNotContains(ignoredType);
            }

            var validationIgnoredTypes = new[] { typeof(Type) };
            foreach (var ignoredType in validationIgnoredTypes)
            {
                Configuration.Validation.IgnoredTypes.AddIfNotContains(ignoredType);
            }

        }

        private void RegisterMissingComponents()
        {
            if (!IocManager.IsRegistered<IGuidGenerator>())
            {
                IocManager.Register<IGuidGenerator>(SequentialGuidGenerator.Instance);
            }

            IocManager.RegisterIfNot<IUnitOfWork, NullUnitOfWork>(LifetimeScopeStyle.Transient);
            IocManager.RegisterIfNot<IAuditingStore, SimpleLogAuditingStore>(LifetimeScopeStyle.Singleton);
            IocManager.RegisterIfNot<IPermissionChecker, NullPermissionChecker>(LifetimeScopeStyle.Singleton);
            IocManager.RegisterIfNot<IUnitOfWorkFilterExecuter, NullUnitOfWorkFilterExecuter>(LifetimeScopeStyle.Singleton);

            if (Configuration.BackgroundJobs.IsJobExecutionEnabled)
            {
                IocManager.RegisterIfNot<IBackgroundJobStore, InMemoryBackgroundJobStore>(LifetimeScopeStyle.Singleton);
            }
            else
            {
                IocManager.RegisterIfNot<IBackgroundJobStore, NullBackgroundJobStore>(LifetimeScopeStyle.Singleton);
            }

        }
    }
}